package gupao.concurrency.primenum;

import gupao.concurrency.utils.SigarUtil;
import gupao.concurrency.utils.ThreadPoolService;
import gupao.concurrency.utils.Utils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class PrimeTest {

    static int concurrency = 64;
    static int partitions = concurrency;
    static int maxNumber = 30000000;
    static ThreadPoolExecutor executor = ThreadPoolService.
            getOrCreateThreadPoolExecutor("prime",concurrency,concurrency);
    static PrimeCalculator calculator = new PrimeCalculator();
    static ConcurrentHashMap<Long, String> threadIdNameMap = new ConcurrentHashMap<>(512);
    public static void main(String[] args) {
        Utils.warmUpThreads(executor, threadIdNameMap, concurrency);
        int chunkPerPartition = maxNumber / partitions;
        List<Future<Integer>> list = new ArrayList<>();
        int lo = 0, hi = 0;

        List<Callable<Integer>> taskList = new ArrayList<>();

        for( int i = 0; i < partitions; i++){
            lo = i * chunkPerPartition;
            hi = lo + chunkPerPartition;
            final int loInUse = lo, hiInUse = hi;
            taskList.add(() -> calculator.countPrimeInRange(loInUse,hiInUse));
        }
        if(hi <= maxNumber){
            int hiInUse = hi;
            taskList.add(() -> calculator.countPrimeInRange(hiInUse, maxNumber+1));
        }
        long start = System.currentTimeMillis();

        long prvThreadTime = SigarUtil.calcThreadsTime(threadIdNameMap.keySet());
        long prvCpuTime = SigarUtil.calcCpuTime();
        for (Callable<Integer> task : taskList) {
            Future<Integer> fs = executor.submit(task);
            list.add(fs);
        }
        int total = waitAndCollectTotal(list);
        long timeCost = System.currentTimeMillis() - start;
        long curThreadTime = SigarUtil.calcThreadsTime(threadIdNameMap.keySet());
        long curCpuTime = SigarUtil.calcCpuTime();
        long threadsTimeCost = (curThreadTime - prvThreadTime)/1000000;
        long cpuTimeCost = curCpuTime - prvCpuTime;
        System.out.println("concurrency: " + concurrency + ", partition number: " + partitions);
        System.out.println("find prime number: " + total + ", time cost: " + timeCost);
        System.out.println("Threads time: " + threadsTimeCost);
        System.out.println("CPU time: " + cpuTimeCost);
        double ratio = threadsTimeCost * 1.0D / cpuTimeCost;
        System.out.println("CPU Ratio: " + ratio);
    }

    private static int waitAndCollectTotal(List<Future<Integer>> list){
        int rt = 0;
        for (Future<Integer> fs : list) {
            try {
                rt += fs.get(1, TimeUnit.MINUTES);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return rt;
    }
}
